package DBLag;

import ModelLag.Shift;
import ModelLag.WorkingPlan;
import java.sql.*;
import java.util.ArrayList;
import java.util.GregorianCalendar;

/**
 * Databaseklasse for WorkingPlan som står for kommunikationen til databasen og
 * for opbygningen af WorkingPlan objekter som skal bruges i de højere lag.
 * @author DM75 Gruppe 3
 * @version 2011-12-22
 */
public class DbWorkingPlan implements IFDbWorkingPlan
{
    // Forbindelse til databasen.
    private Connection con;
    // En instans variabel af typen PreparedStatement som indeholder et PreparedStatement
    // til brug ved findmetode.
    private PreparedStatement pStmtSelect;
    private PreparedStatement pStmtWPsThisMonth;
    // Slå print til og fra.
    private boolean debug = false;
    
    public DbWorkingPlan()
    {
        con = DbConnection1.getInstance().getDBcon();
        pStmtSelect = null;
        pStmtWPsThisMonth = null;
    }
    
    /**
     * Henter det højeste WorkingPlanID i WorkingPlan tabellen i databasen og ligger 1 til for
     * at lave et nyt WorkingPlanID til en ny række.
     * @return WorkingPlanID til ny række.
     */
    private int getNextWorkingplanID()
    {
        String query = null;
        ResultSet results = null;
        int nextInt =0;
        try
        {
            query = "SELECT MAX(workingPlanID) FROM WorkingPlan";
            PreparedStatement preStmt = con.prepareStatement(query);
            results = preStmt.executeQuery();
        
        if(results.next())
        {
            nextInt = results.getInt(1);
            nextInt++;
        }
        return nextInt; 
        
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
            return -1;
        }
    }
    
    // pStmtSelect må ikke lukkes(.close) i findWorkingPlan(), da man bruger det hver gang man skal finde et WorkingPlan objekt.
    /**
     * Finder en WorkingPlan i databasen og sørger for at bliver bygget op til et
     * objekt ved hjælp af buildWorkingPlan() og dato for dagen, afdeling 
     * @param date GregorianCalendar objekt for den ønskede dag.
     * @param location Afdeling i virksomheden.
     * @param retriveAssociation true = associering laves til Employee objekter for
     * hvert shift objekt. 
     * @return Hvis fundet returneres et WorkingPlan objekt ellers null.
     */
    @Override
    public WorkingPlan findWorkingPlan(GregorianCalendar date, String location, 
                                       boolean retriveAssociation)
    {
        if(pStmtSelect == null)
        {
            pStmtSelect = getPreparedStmt();
        }
        WorkingPlan workingPObj = null;
        ResultSet results = null; 
        if(debug)
        {
            System.out.println("findWorkingPlan");
        }
        try
        {
            // Konvertering fra Java til SQL (dato).
            java.sql.Date tempDate = new java.sql.Date(date.getTimeInMillis());
            pStmtSelect.setDate(1, tempDate);
            pStmtSelect.setString(2, location);
            results = pStmtSelect.executeQuery();
            if(results.next())
            {
                workingPObj = buildWorkingPlan(results);
            }
            if(retriveAssociation)
            {
                IFDbShift dbShift = new DbShift();
                // true fordi der ønskes at få alle de shift og for hver shift  
                //deres employee.
                workingPObj.setShifts(
                   dbShift.getShiftsWithThisDateLocation(date, location, true));
            }
        }
        catch(SQLException sqlE)
        {
            System.out.println("SQL Error");
            System.out.println(sqlE.getMessage());
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
        return workingPObj; 
    }
    
    /**
     * Finder workingPlanID ud fra dato og afdeling.
     * @param date GregorianCalendar objekt for dagen.
     * @param location Afdeling i virksomeheden.
     * @return workingPlanID for WorkingPlan.
     */
    public int getWorkingPlanID(GregorianCalendar date, String location)
    {
        ResultSet results = null;
        int workingPlanID = -1;
        
        try
        {
            String query = "SELECT * FROM WorkingPlan WHERE date = ? AND location = ?";
            PreparedStatement preStmt = con.prepareStatement(query);
            
            java.sql.Date tempDate = new java.sql.Date(date.getTimeInMillis());
            preStmt.setDate(1, tempDate);
            preStmt.setString(2, location);
            
            results = preStmt.executeQuery();
            
            if(results.next())
            {
                workingPlanID = results.getInt("workingPlanID");
            }
            
            preStmt.close();
        }
        catch(SQLException sqlE)
        {
            System.out.println("SQL Error");
            System.out.println(sqlE.getMessage());
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
        return workingPlanID;
    }     
    
    
    /**
     * Henter alle rækker i Workingplan tabellen fra databasen og bygger dem op til
     * objekter ved hjælp af buildWorkingPlan() og derefter tilføjes hvert objekt tl en
     * ArrayList.
     * @param retriveAssociation true = associering i ArrayList med alle shifts som får tilknyttet Employee.
     * @return 
     */
    public ArrayList<WorkingPlan> getAllWorkingPlans(boolean retriveAssociation)
    {
        ArrayList<WorkingPlan> list = new ArrayList<WorkingPlan>();
        PreparedStatement pstmt = null;
        
        
        String allWorkingPlans = "SELECT * "
                               + "FROM WorkingPlan";
        if(debug)
        {
            System.out.println(allWorkingPlans);
        }
        
        ResultSet results = null;
        WorkingPlan workingPObj = null;
        try
        {
            pstmt = con.prepareStatement(allWorkingPlans);
            pstmt.setQueryTimeout(5);
            results = pstmt.executeQuery();
            
            while(results.next())
            {
                workingPObj = buildWorkingPlan(results);
                
                if(retriveAssociation)
                {
                    GregorianCalendar date = new GregorianCalendar();
                    date.setTimeInMillis(results.getDate("date").getTime());
                    String location = results.getString("location");
                    
                    IFDbShift dbShift = new DbShift();
                    // true = Associering til Shifts for denne WorkingPlan og for hver Shift en Employee.
                    workingPObj.setShifts(dbShift.getShiftsWithThisDateLocation(date, location, true));
                }
                
                list.add(workingPObj);  
            }
            pstmt.close();
        }
        catch(SQLException sqlE)
        {
            System.out.println("SQL Error");
            System.out.println(sqlE.getMessage());
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
        
        return list;
    
    }
    
    /**
     * Henter alle WorkingPlans med denne måned og lokalitet.
     * @param month Måned: 1-12.
     * @param searchLocation Afdeling i virkomheden.
     * @param retriveAssociation Associeringer til Shift objekter.
     * @return ArrayListe med WorkingPlan objekter.
     */
    public ArrayList<WorkingPlan> getWorkingPlansMonthLoc(int month, int year, String searchLocation, boolean retriveAssociation)
    {
        if(pStmtWPsThisMonth == null)
        {
            pStmtWPsThisMonth = getPreparedStatementWPsMonthLoc();
        }
        if(debug)
        {
            System.out.println("getWorkingPlansMonthLoc");
        }
        ArrayList<WorkingPlan> list = new ArrayList<WorkingPlan>();
        
        ResultSet results = null;
        WorkingPlan workingPObj = null;
        try
        {
            pStmtWPsThisMonth.setInt(1, month);
            pStmtWPsThisMonth.setInt(2, year);
            pStmtWPsThisMonth.setString(3, searchLocation);
            results = pStmtWPsThisMonth.executeQuery();
            
            while(results.next())
            {
                workingPObj = buildWorkingPlan(results);
                
                if(retriveAssociation)
                {
                    GregorianCalendar date = new GregorianCalendar();
                    date.setTimeInMillis(results.getDate("date").getTime());
                    String location = results.getString("location");
                    
                    IFDbShift dbShift = new DbShift();
                    // true = Associering til Shifts for denne WorkingPlan og for hver Shift en Employee.
                    workingPObj.setShifts(dbShift.getShiftsWithThisDateLocation(date, location, true));
                }
                
                list.add(workingPObj);  
            }
        }
        catch(SQLException sqlE)
        {
            System.out.println("SQL Error");
            System.out.println(sqlE.getMessage());
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
        
        return list;
    }
    
    
    /**
     * Indsætter et WorkingPlan objekt som en række i WorkingPlan tabellen i databasen.
     * @param workingPObj Det objekt som ønskes indsat i WorkingPlan tabellen i databasen.
     * @return -1 = mislykkedes. 1 = lykkedes.
     */
    public int insertWorkingPlan(WorkingPlan workingPObj)
    {
        int rc = -1;
        PreparedStatement pstmt = null;
        String insert = "INSERT INTO WorkingPlan (workingPlanID, date, location) "
                      + "VALUES (?,?,?)";
        if(debug)
        {
            System.out.println(insert);
        }
        
        if(getNextWorkingplanID() != -1)
        {
            try
            {
                pstmt = con.prepareStatement(insert);
                pstmt.setQueryTimeout(5);
                pstmt.setInt(1, getNextWorkingplanID());
                // Fra Java til SQL (dato)
                java.sql.Date tempDate = new java.sql.Date(workingPObj.getDate().getTimeInMillis());
                pstmt.setDate(2,tempDate);
                pstmt.setString(3, workingPObj.getLocation());
                rc = pstmt.executeUpdate();
                pstmt.close();
            }
            catch(SQLException sqlE)
            {
                System.out.println("SQL Error");
                System.out.println(sqlE.getMessage());
            }
            catch(Exception e)
            {
                System.out.println(e.getMessage());
            }
            return rc;
        }
        else
        {
            System.out.println("FEJL i getNextWorkingPlanID()");
            return rc;
        }   
    }
    
    /**
     * Sletning af en WorkingPlan. Kan kun lade sig at gøre hvis den er tom, dvs.
     * uden shifts da shift.workingPlanID er fremmednøgle til WorkingPlan.workingPlanID.
     * Dette kan dog løses ved at indføre cascade således at hvis en WorkingPlan slettes
     * Så slettes alle shifts med det samme workingPlanID som WorkingPlans også.
     * @param date Dato hvor workingplan ønskes slettet.
     * @param location Afdeling for workingplan.
     * @return -1 = mislykkedes. 1 = lykkedes.
     */
    public int deleteWorkingPlan(GregorianCalendar date, String location)
    {
        int rc = -1;
        PreparedStatement pstmt = null;
        String delete = "DELETE FROM WorkingPlan WHERE date = ? AND location = ?";
        
        if(debug)
        {
            System.out.println(delete);
        }
        
        try
        {
            pstmt = con.prepareStatement(delete);
            pstmt.setQueryTimeout(5);
            // Fra Java til SQL (dato)
            java.sql.Date tempDate = new java.sql.Date(date.getTimeInMillis());
            pstmt.setDate(1, tempDate);
            pstmt.setString(2, location);
            rc = pstmt.executeUpdate();
            
            pstmt.close();
        }
        catch(SQLException sqlE)
        {
            System.out.println("SQL Error");
            System.out.println(sqlE.getMessage());
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
        
        return rc;
    }
    
    /**
     * Finder de Employees som allerede arbejder i det angivne tidsrum. Skal bruges
     * til at finde ud af hvem der kan arbejde i det angivne tidsrum.
     * @param date Den dato Shiften skal laves.
     * @param newShiftStart Startid (HH:MM)
     * @param newShiftEnd Sluttid (HH:MM)
     * @return ArrayList med CPR-NUMMER på dem som ikke kan arbejde.
     */
    public ArrayList<String> EmployeesWhoAlreadyWorkInThisPeriod(GregorianCalendar date, String newShiftStart, String newShiftEnd)
    {
        ArrayList<String> cantWork = new ArrayList<String>();
        
        PreparedStatement pstmt = null;
        
        String whoCantWork = "SELECT DISTINCT cprNo "
                           + "FROM Shift, WorkingPlan "
                           + "WHERE date = ? AND ((startTime <= ? OR startTime < ?) AND (endTime > ? OR endTime >= ?)) "
                           + "AND Shift.workingPlanID = WorkingPlan.workingPlanID";
        
        if(debug)
        {
            System.out.println(whoCantWork);
        }
        
        String cprNofound = "";
        ResultSet results = null;
        
        try
        {
            pstmt = con.prepareStatement(whoCantWork);
            pstmt.setQueryTimeout(5);
            // Konverter fra JAVA til SQL (dato)
            java.sql.Date tempDate = new java.sql.Date(date.getTimeInMillis());
            pstmt.setDate(1, tempDate);
            pstmt.setString(2, newShiftStart);
            pstmt.setString(3, newShiftEnd);
            pstmt.setString(4, newShiftStart);
            pstmt.setString(5, newShiftEnd);
            
            results = pstmt.executeQuery();
            
            while(results.next())
            {
                cprNofound = results.getString("cprNo");
                cantWork.add(cprNofound);
            }
            pstmt.close(); 
        }
        
        catch(SQLException sqlE)
        {
            System.out.println("SQL Error");
            System.out.println(sqlE.getMessage());
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
        
        return cantWork;
    }

    /**
     * Opbygning af WorkingPlan objekt sker i denne metode.
     * @param results ResultSet. En form for tabel med resultateterne i fra det
     * PreparedStatement som er sendt til databasen.
     * @return WorkingPlan objekt.
     */
    private WorkingPlan buildWorkingPlan(ResultSet results)
    {
        WorkingPlan workingPObj = new WorkingPlan();
        ArrayList<Shift> shiftsOnThisWPlan = new ArrayList<Shift>();
        GregorianCalendar newCal = new GregorianCalendar();
        try
        { 
            //Konvertering fra SQL til Java (DATO).
            newCal.setTimeInMillis(results.getDate("date").getTime());
            workingPObj.setDate(newCal);
            workingPObj.setLocation(results.getString("location"));
            workingPObj.setShifts(shiftsOnThisWPlan); 
        }
        catch(Exception e)
        {
            System.out.println("Error in building workingplan object");
            System.out.println(e.getMessage());
        }
        return workingPObj;
    }
    
    /**
     * PreparedStatement som bruges til findmetoden. Dette laves en gang og gemmes
     * derefter i en instans variabel, da dette preparedStatement bruges ofte kan
     * dette betale sig at gemme og derved går det hurtigere end hvis man skal til at
     * lave et nyt hver gang.
     * @return PreparedStatement objekt.
     */
    private PreparedStatement getPreparedStmt()
    {
        try
        {
            String query = "SELECT date, location "
                         + "FROM WorkingPlan "
                         + "WHERE date = ? AND location = ?";
            if(debug)
            {
                System.out.println("Preparestatement " + query);
            }
            pStmtSelect = con.prepareStatement(query);
            pStmtSelect.setQueryTimeout(5);
        }
        catch(SQLException sqlE)
        {
            System.out.println("SQL Error");
            System.out.println(sqlE.getMessage());
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
        return pStmtSelect;
    }
    
    /**
     * PreparedStatement som bruges til en metode for at få alle Workingplans i en
     * specifik måned og lokalitet. Dette laves en gang og gemmes derefter i en instans variabel,
     * da dette preparedStatement bruges ofte kan dette betale sig at gemme og derved
     * går det hurtigere end hvis man skal til at lave et nyt hver gang.
     * @return PreparedStatement objekt.
     */    
    private PreparedStatement getPreparedStatementWPsMonthLoc()
    {
        try
        {
            String query = "SELECT date, location "
                         + "FROM WorkingPlan " 
                         + "WHERE MONTH(date) = ? AND YEAR(date) = ? AND location = ?";
            if(debug)
            {
                System.out.println("PreparestatementWPMonth " + query);
            }
            
            pStmtWPsThisMonth = con.prepareStatement(query);
            pStmtWPsThisMonth.setQueryTimeout(5);
        }
        
        catch(SQLException sqlE)
        {
            System.out.println("SQL Error");
            System.out.println(sqlE.getMessage());
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
        return pStmtWPsThisMonth;
    }
}

